{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ecd281d4-e3df-4e8e-aa98-45743e3b8c1b",
   "metadata": {},
   "source": [
    "# **深度学习公开课 - 深度学习中的时间序列算法群**\n",
    "> 节选自《深度学习实战》第7期正课<br>\n",
    "> 作者：@菜菜TsaiTsai<br>\n",
    "> 版本号：2023/10/27<br>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22fd7f9a-dab5-4f07-b9e6-bb45de3175fc",
   "metadata": {},
   "source": [
    "## **<center><font color =\"k\">公开课加餐 LSTM原理与数学精讲<br><br>极致易学 | 高效入门 | 前景讨论<center>**<br>**<center><font color =\"red\">直播将于8点45分正式开始！<br><br>扫码回复\"DL999\"领取今日直播课件>>><br><br>扫码回复\"优惠\"抢直播间专属优惠券>>></font></center>**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a1b52da-1768-434b-b3b4-913c18794a66",
   "metadata": {},
   "source": [
    "## 0 课程规划\n",
    "\n",
    "欢迎来到《深度学习中的时间序列算法群》公开课。在这门课程中，我将带你从0认识3大类深度学习中的时间序列模型，并为你深度讲解深度时序算法众多的的精彩理念与实现方式。当你完成这门课程时，你将完成深度时序算法入门，打好进一步学习更多高级架构的基础。\n",
    "\n",
    "**DAY 1：LSTM与深度学习中的时间序列**\n",
    "1. 深度学习中的时间序列数据\n",
    "2. 时序数据 vs 非时序数据\n",
    "3. 循环神经网络如何处理时序问题\n",
    "4. LSTM的灵感起源与直觉理解\n",
    "5. LSTM的基本结构与架构设计\n",
    "\n",
    "**DAY 2：LSTM的参数全解与预测实战**\n",
    "1. PyTorch中的LSTM层与参数\n",
    "2. LSTM类的输入与输出\n",
    "3. 股价与时间序列数据预测实战\n",
    "\n",
    "**DAY 3：时序进阶：时序卷积网络TCN**\n",
    "1. 1d卷积操作与时序数据\n",
    "2. 因果卷积 Causal Convolution\n",
    "3. 1d膨胀卷积与感受野扩张\n",
    "4. 时序卷积网络的架构与实现\n",
    "5. 深度学习中的5大类时序解决方案\n",
    "\n",
    "**DAY 4：Transformer与Attention用于时间序列**\n",
    "\n",
    "**DAY 5：Informer架构解析与时序应用**\n",
    "\n",
    "**DAY 6：深度时序SOTA架构TabNet**\n",
    "\n",
    "更多后续课程请关注B站动态和小可爱私聊信息！"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b516dd07-e34f-4908-9b1c-8910f7e6eccb",
   "metadata": {
    "tags": []
   },
   "source": [
    "![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A011.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47d3638d-6665-45ac-983d-9e6f7a666104",
   "metadata": {
    "jupyter": {
     "source_hidden": true
    },
    "tags": []
   },
   "source": [
    "![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A05.png)\n",
    "\n",
    "![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A06.png)\n",
    "\n",
    "![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A09.png)\n",
    "\n",
    "|||\n",
    "|:-:|:-:|\n",
    "|![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A01.png)|![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A07.png)|\n",
    "\n",
    "|||\n",
    "|:-:|:-:|\n",
    "|![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A02.png)|![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A08.png)|"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "973ece9b-bc1c-4568-a438-8c615f689a55",
   "metadata": {},
   "source": [
    "## 1 学前自测"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "406cd729-29ec-4670-8b5b-04afed52acdc",
   "metadata": {},
   "source": [
    "**【Q1】你了解深度学习中的时间序列吗？比如，时间序列一般是几维的数据？每个维度的名字是什么，通常代表了什么含义？**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6611bf33-2c6c-4598-9065-9b73e77548fd",
   "metadata": {
    "tags": []
   },
   "source": [
    "时间序列可以是一维、二维、三维甚至更高维度的数据，在深度学习的世界中常见的是三维时间序列，这三个维度分别是（batch_size, time_step, input_dimensions）。\n",
    "\n",
    "![](http://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/Live/NLP%26LLMs/03_.png)\n",
    "\n",
    "其中time_step是时间步，它是时间序列中的序列长度（sequence_length）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4ffbfc9-71e9-4564-b288-bba56236e1d5",
   "metadata": {},
   "source": [
    "**【Q2】为什么普通的机器学习/深度学习算法无法处理时间序列数据？你了解时序算法设计过程中的核心需求吗？**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "716de339-1d4b-4020-8a23-2bfc73add4b5",
   "metadata": {
    "tags": []
   },
   "source": [
    "时间序列是一种序列数据。序列数据是样本与样本之间有着特定的逻辑联系的数据，序列数据通常按照样本与样本之间的逻辑顺序排列，且这种顺序不能被轻易修改和打乱的数据。在序列数据上，**我们不仅要学习特征与标签之间的关联，还要学习样本与样本之间的关联**，因为序列数据中的样本会根据排列顺序影响彼此，并最终影响到标签的输出和算法的结果。\n",
    "\n",
    "大多机器学习和深度学习算法假设样本与样本之间是相互独立的，因此大部分算法只能够学习到特征与标签之间的关系，而对样本与样本之间的联系无能为力，这是普通机器学习算法不适用于时间序列数据的关键。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5185927b-bb12-4083-8f69-11dd54495374",
   "metadata": {},
   "source": [
    "**【Q3】你知道循环类神经网络，尤其是RNN处理时间序列数据的思路吗？**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5b1bbd5a-caab-4bfd-bdb3-7c9f23c03a6d",
   "metadata": {
    "tags": []
   },
   "source": [
    "对时间序列数据而言，样本与样本之间的关系就是上一个时间点与下一个时间点之间的关系，因此**循环神经网络家族采取的手段是——依次处理时间点、并将上一个时间点的信息传递融入下一个时间点的信息的运算过程，使得下一个时间点能够获得上一个时间点传来的信息，从而在两个时间点中建立联系**。这是早年深度学习算法在处理时间序列数据时的一般思路。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "81d0d305-cbee-4308-a0be-3bfe2896439a",
   "metadata": {},
   "source": [
    "**【Q4】RNN存在哪些问题？LSTM的原作者是基于什么基本思路构建LSTM的结构的？**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bb4169bb-47b3-4613-ad17-10b39f34b5a2",
   "metadata": {
    "tags": []
   },
   "source": [
    "RNN算法思路新颖，但是却存在众多实践中的问题，其中“记忆机制”相关的问题十分显著，包括但不限于——\n",
    "\n",
    "- **RNN对所有信息照单全收，记忆机制中毫无重点**\n",
    "\n",
    "从常识来判断，无论是在时间序列中还是在文字序列中，过去时间步上的信息对未来时间步上信息的影响可能是不同的。但RNN为了节约计算资源，设置了全样本权重共享机制，这意味着RNN处理所有时间步信息的方式是完全相同的。因此，在RNN吸纳信息时，它**无法辨别不同时间点上的信息对未来的不同影响**，在使用这些信息进行预测的时候，RNN**也无法辨别不同时间步上的标签最需要的信息是哪些**，这会严重影响算法的整体预测效果。到今天，这依然是影响RNN预测效果的关键因素之一。\n",
    "\n",
    "- **RNN中的新信息会强制覆盖旧信息，导致长期记忆被遗忘**\n",
    "\n",
    "虽然循环网络家族建立起了时间点之间的联系，但这种联系有时不总是有效的，当一个时间点上包含的历史时间点信息越多，那些久远的历史时间点的信息就不太容易对当下的预测构成影响。在RNN的计算流程中，虽然理论上最后一个时间步的$H_T$中包含了所有时间步上的信息，但是真正有影响力的只要非常接近最后一个时间步的几个时间步而已。**大部分信息都被RNN遗忘，导致RNN很难处理较长的序列**。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d2266c1-4395-4def-9af4-65a0a9360f96",
   "metadata": {},
   "source": [
    "基于以上两点问题，研究者们在设计LSTM的时候存在一个根本诉求——<font color=\"red\">**要创造一个全新的架构、一套全新的数据流，为循环神经网络赋予选择性记忆和选择性传递信息的能力**</font>。这里的选择性包含多层含义，包括：\n",
    "\n",
    "**1. 循环网络必须自行选择吸纳多少新信息**，只留重点，拒绝照单全收\n",
    "\n",
    "**2. 循环网络必须自行选择遗忘多少历史信息**，主动遗忘无效内容，保留有效内容\n",
    "\n",
    "**3. 循环网络必须自行判断、对当前时间步的预测来说最重要的信息是哪些，并将该信息输出给当前时间步**，这样既可以保证当前时间步上的预测是最高效的，也不会影响向下一个时间步传递的信息。\n",
    "\n",
    "在这三个能力当中，前两个能力允许循环神经网络决定“哪些长期信息会被传递下去”，最后一个能力允许循环神经网络决定“哪些短期信息对当前时间步的预测是最重要的”。这三种能力构成了LSTM的基本结构。接下来就让我们来看看LSTM是如何实现以上三种能力的。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f49f6163-f62d-48f5-b926-dfa455784968",
   "metadata": {},
   "source": [
    "## 2 LSTM基本架构与原理"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58104c4a-5a50-4ecd-85e9-eebb63b24730",
   "metadata": {},
   "source": [
    "LSTM算法的结构本身十分繁复，无论是从网络架构还是数学原理角度出发，我们都很难使用简单的语言将LSTM完整呈现。然而幸运的是，在了解研究者们要为RNN增加的三大类能力后，我们会发现LSTM的思路是非常清晰的。让我们一起来看看LSTM的基本结构——"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d905ae6-9d87-4e1c-aaf7-33a7824e7da2",
   "metadata": {},
   "source": [
    "- 记忆细胞"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74cb8c8c-cc1a-407e-99ed-d5974bf82326",
   "metadata": {},
   "source": [
    "首先，**LSTM依然是一个循环神经网络，因此LSTM的网络架构与RNN高度相似，同时LSTM也是需要遍历所有时间步，不断完成循环和嵌套的**。但不同的是，RNN由输入层（输入$X_t$）、隐藏层和输出层（输出$Y_t$）构成，而LSTM由输入层（输入$X_t$）、**记忆细胞（Memory Cell）**和输出层（输出$Y_t$）构成，其中输入、输出层与RNN的输入、输出层完全一致，而记忆细胞是LSTM独有的结构。\n",
    "\n",
    "![](http://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/02_.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27e138d7-19b7-421b-8adc-f80a80e40f6c",
   "metadata": {},
   "source": [
    "**记忆细胞是LSTM的基本计算单元，在记忆细胞中，我们分割长期信息与短期信息，同时赋予循环网络对信息做选择的能力**。在之前我们提到，循环网络必须自行决定哪些长期信息会被传递下去，哪些短期信息对当前的预测最为有效，因此在记忆细胞当中，LSTM设置了两个关键变量：\n",
    "\n",
    "> - **主要负责记忆短期信息、尤其是当前时间步信息的隐藏状态$h$，**以及\n",
    "> - **主要负责长期记忆的细胞状态$C$**\n",
    "\n",
    "这两个变量都随着时间步进行迭代。如上图所示，在迭代开始时，LSTM会同时初始化$h_0$和$C_0$；在任意时间步t上，记忆细胞会同时接受到来自上一个时间步的长期记忆$C_{t-1}$、短期信息$h_{t-1}$以及当前时间步上输入的新信息$X_t$三个变量，结合三者进行运算后，记忆细胞会输出当前时间步上的长期记忆$C_{t}$和短期信息$h_{t}$，并将它们传递到下一个时间步上。同时，在每个时间步上，$h_t$还会被传向当前时间步的输出层，用于计算当前时间步的预测值$\\hat{y}_t$。\n",
    "\n",
    "那在记忆细胞的内部，究竟是如何进行具体的$C_t$和$h_t$计算的呢？让我们来看看记忆细胞内部的流程图：\n",
    "\n",
    "![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/LSTM%E6%B5%81%E7%A8%8B%E5%9B%BE1.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "928a886a-989c-4103-8c25-e08d56f196db",
   "metadata": {},
   "source": [
    "这是一个复杂的流程，但在横向上，它可以被分割为C的传递和h的传递两条路径；在纵向上，它可以被分为如图所示的三个不同的路径：\n",
    "\n",
    "**1. 帮助循环网络选择吸纳多少新信息的输入门**\n",
    "\n",
    "**2. 帮助循环网络选择遗忘多少历史信息的遗忘门**，以及\n",
    "\n",
    "**3. 帮助循环网络选择出对当前时间步的预测来说最重要的信息、并将该信息输出给当前时间步的输出门**\n",
    "\n",
    "![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/LSTM%E6%B5%81%E7%A8%8B%E5%9B%BE2.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "874d13a0-e2e5-423c-8a38-601108321e38",
   "metadata": {},
   "source": [
    "让我们分别来看一下三个门是如何工作的。\n",
    "\n",
    "- **遗忘门**\n",
    "\n",
    "**遗忘门是决定要留下多少长期信息C的关键计算单元，其数学本质是令上一个时间步传入的$C_{t-1}$乘以一个[0,1]之间的比例，以此筛选掉部分旧信息。**在这个计算过程中，假设遗忘门令$C_{t-1}$乘以0.7，那就是说遗忘门决定了要保留70%的历史信息，遗忘30%的历史信息，这30%的信息空间就可以留给全新的信息来使用。\n",
    "\n",
    "![](http://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/09.png)\n",
    "\n",
    "那这个比例是如何被计算出来的呢？如图所示，遗忘门会参考当前时间步的信息$X_t$与上一个时间步的短时信息$h_{t-1}$来计算该比例，其中$\\sigma$是sigmoid函数，$w_f$是动态影响最终权重大小的参数，$f_t$就是[0,1]之间的、用于影响$C_{t-1}$的比例。\n",
    "\n",
    "在LSTM的设计逻辑之中，考虑$X_t$和$h_{t-1}$实际是在考虑离当前时间步最近的**上下文信息**，而权重$w_f$会受到**损失函数和算法整体表现的影响，不断调节遗忘门中计算出的比例f的大小**，因此遗忘门能够结合上下文信息、损失函数传来的梯度信息、以及历史信息共同计算出全新的、被留下的长期记忆$C_t$。这个流程在实践中被证明是十分有效的。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0f0f468-e1e8-4900-b9e4-5f778928caad",
   "metadata": {},
   "source": [
    "- **输入门**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc9f520b-b381-4460-b919-78ed60192909",
   "metadata": {},
   "source": [
    "**输入门是决定要吸纳多少新信息来融入长期记忆C的计算单元，其数学本质是在当前时间步传入的所有信息上乘以一个[0,1]之间的比例，以筛选掉部分新信息，将剩余的新信息融入长期记忆C**。\n",
    "\n",
    "在这个计算过程中，我们首先要计算出当前时间步总共吸收了多少全新的信息$\\tilde{C}_t$，这个计算全新信息的方式就与RNN中计算$h_t$的方式高度相似，因此也会包含用于影响新信息传递的参数$W_C$和RNN中常见的tanh函数。然后，我们要依据上下文信息（依然是$X_t$和$h_{t-1}$）以及参数$W_i$来生成筛选新信息的比例$i_t$。最后我们将二者相乘，并加入到长期记忆$C$当中。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3215a80b-5059-4120-8665-358c3d10687d",
   "metadata": {},
   "source": [
    "![](http://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/07.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9cca597c-4247-40c2-84cd-7bd265716c2b",
   "metadata": {},
   "source": [
    "可以看到，相比起RNN的数据输入过程，LSTM的输入过程灵活了非常多——在输入门当中，我们不仅对输入数据加上了一个比例$i_t$，还分别使用了两个受损失函数影响的权重$W_i$和$W_C$来控制新信息聚合和比例计算的流程。在这一比例和两大参数的作用下，输入数据可以被高度灵活地调节，以便满足最佳的损失函数需求。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1d949a33-84b4-4399-8207-f3dbf7933741",
   "metadata": {},
   "source": [
    "- **更新细胞状态**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f00d1ba3-277f-4611-852d-557b71cfd636",
   "metadata": {},
   "source": [
    "当遗忘门决定了哪些信息要被遗忘，输入门决定了哪些信息要被加入到长期记忆后，就可以更新用于控制长期记忆的细胞状态了。如下图所示，上一个时间步的长期记忆将乘以遗忘门给出的比例$f_t$，再加上新信息$\\tilde{C}_t$乘以新信息筛选的比例$i_t$，同时考虑放弃过去的信息、容纳新信息，以此来构成传递给下一个时间步的长期信息$C_t$。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d5fbe909-3fa4-4ca3-87af-1e62d1d8fdea",
   "metadata": {},
   "source": [
    "![](http://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/03.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f887933-f361-4fbb-9f55-2a6028e96240",
   "metadata": {},
   "source": [
    "- **输出门**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a15246d9-2fa5-46e5-84d2-6a8495d88eb6",
   "metadata": {},
   "source": [
    "最后我们来到了输出门。**输出门是从全新的长期信息$C_t$中筛选出最适合当前时间步的短期信息$h_t$的计算单元，其数学本质是令已经计算好的长期信息$C_t$乘以一个[0,1]之间的比例，以此筛选出对当前时间步最有效的信息用于当前时间步的预测**。具体流程如下所示："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2cb7705-863e-43e3-9c28-a199dd04de6b",
   "metadata": {},
   "source": [
    "![](http://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/08.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4033706e-389e-4696-88ea-fe69220c0696",
   "metadata": {},
   "source": [
    "这个流程分为三步:\n",
    "1. 首先要借助上下文信息和权重$W_o$来求解出比例$o_t$\n",
    "2. 对长期信息$C_t$进行tanh标准化处理\n",
    "3. 将$o_t$乘在标准化后的长期信息$C_t$之上，用于筛选出$h_t$。\n",
    "\n",
    "为什么要对长期信息$C_t$做标准化处理呢？在LSTM的论文中如此写到：Tanh标准化可以限制有效长期信息$C_t$的数字范围，避免历史信息在传递过程中变得越来越大，同时还能为输出门赋予一定的非线性性质，这个过程被实践证明有助于保持训练稳定、还能够强化算法学习能力，因此在LSTM最终的设计中被保留下来。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0b36222-74b5-4146-9444-2fbee6a86fa6",
   "metadata": {},
   "source": [
    "![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/01.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "140daac4-405e-467b-aa4d-ed64df239db6",
   "metadata": {},
   "source": [
    "这就是记忆细胞的全部数学过程。在这个过程中我们能够看到LSTM是如何为RNN赋予了各种灵活的能力，从而实现了多个层次上的“信息筛选”的。通过分割长短期信息、以及设置门控单元的方式，LSTM有效改善了循环神经网络在信息筛选方面的困境。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a4e4264-8952-4e58-9a1e-5231e38f64e2",
   "metadata": {},
   "source": [
    "- **【深度】LSTM（拓展到深度学习）中的各个数学流程是如何各司其职的？**\n",
    "\n",
    "在LSTM中，我们设计了长期记忆$C$和短期信息$h$，同时还设计了遗忘、输入、输出三大门控单元。但这些机制是如何起到和它们的名字类似的作用的呢？比如，残差链接中的残差在哪里？遗忘门真的能控制遗忘、输出门真的能控制输出吗？Transformer中的Q矩阵负责查询，K矩阵负责索引，V矩阵负责内容，这些理所当然的“事实”是如何实现的？"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fd327fd5-e4e4-43fd-b38a-8439779852ad",
   "metadata": {},
   "source": [
    "对LSTM算法来说，它当然不会因为自己各个数学流程被取了名字就执行这个名字对应的任务，在遗忘门中LSTM并无法意识到自己给长期记忆乘以一个比例的行为是在进行“遗忘”，它只是在筛选更有效的历史信息而已，因此“遗忘门”这个名字被更换成“记忆门”、“长期记忆筛选门”也没有问题，其本质就是一个筛选历史信息的计算过程。而输出门其实是通过损失函数的反馈来不断调整权重，最终获得有效输出。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50042728-d86f-4d9c-abe1-ae6ca40265c7",
   "metadata": {},
   "source": [
    "关于这一点，我们可以来仔细讨论一下长期记忆$C$和短期记忆$h$。光从流程图来看，长期记忆$C$和短期信息$h$的计算过程似乎高度相似，为何它们能够分别承载长期和短期信息呢？事实上$C$和$h$的计算流程、数学本质都完全不同——\n",
    "\n",
    "![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/LSTM%E6%B5%81%E7%A8%8B%E5%9B%BE1.png)\n",
    "\n",
    "在时间步t上，当记忆细胞接受到$C_{t-1}$、$h_{t-1}$以及$X_t$三个变量后，它会结合新信息$X_t$和短期信息$h_{t-1}$来共同调整长期记忆，并最终将$C_{t-1}$更新为包含了当前时间步信息的长期记忆$C_{t}$。在生成$C_t$后，记忆细胞会依据当前时间步上的标签需求、当前时间步上的输入信息和少部分历史信息、从$C_t$中提取出最利于当前时间步预测的短期信息$h_t$，并将该信息用于当前时间步的预测。所有计算完成后，$C_{t}$和$h_t$都被传向下一个时间步。在这个过程中——\n",
    "\n",
    "> - **$C_{t}$是在$C_{t-1}$基础上直接迭代更新得到的**（这是说，我们可以写出一个形似$C_{t} = w C_{t-1} + b$的公式），所以$C_t$整合了[1,t]所有时间步的历史信息，并负责将这些信息不断传递下去。\n",
    "> \n",
    "> - 但是**，$h_t$和$h_{t-1}$之间没有直接的迭代关系，**虽然二者有一定的联系，但$h_{t-1}$不是构成$h_{t}$的核心。在记忆细胞中，$h_{t-1}$只是用来辅助$C$进行迭代的变量之一，而$h_t$是为了在当前时间步上生成$\\hat{y}_t$而计算出来的全新变量，影响$h_t$具体取值的核心不是上个时间步的信息$h_{t-1}$，而是当前时间步上的输入信息和预测标签的需求，因此$h_t$是一个主要承载短期信息、尤其是当前时间步上信息的变量，它是为了在当前时间步预测出最准确的标签而存在的。\n",
    "\n",
    "在这个过程中，虽然$h_t$的计算也略微参考了部分历史信息，但$h_t$存在的根本是为了服务于当前时间步上的预测行为。当$h_t$到下一个时间步时，它依然只是辅助C进行迭代的变量之一，$h_{t+1}$的生成主要也是为了服务时间步t+1上的预测行为，与$h_t$没有直接的迭代关系。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b419134c-031e-4879-bbc5-4097b11d8480",
   "metadata": {},
   "source": [
    "![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A011.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5659fac2-f236-498b-b2fc-24e5e4ca1133",
   "metadata": {
    "jupyter": {
     "source_hidden": true
    },
    "tags": []
   },
   "source": [
    "![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A05.png)\n",
    "\n",
    "![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A06.png)\n",
    "\n",
    "![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A09.png)\n",
    "\n",
    "|||\n",
    "|:-:|:-:|\n",
    "|![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A01.png)|![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A07.png)|\n",
    "\n",
    "|||\n",
    "|:-:|:-:|\n",
    "|![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A02.png)|![](https://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/LSTM/%E8%AF%BE%E7%A8%8B%E5%AE%A3%E4%BC%A08.png)|"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5fcf276d-463b-4420-bdf0-d8f551858c15",
   "metadata": {},
   "source": [
    "## 3 深度原理进阶：循环网络中的梯度问题"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "73d3e025-ff3a-4552-8582-d61a53e4c92c",
   "metadata": {},
   "source": [
    "【深度原理进阶】是《深度学习实战》第七期课程中的特色章节，为前沿理论研究打造——\n",
    "\n",
    "- 真正深度认知原理，了解事实本质，助力于学术研究\n",
    "- 从“是/做什么”到“怎么用”再到“为什么”\n",
    "- 学术研究的思路、发论文的思路究竟从哪里来？你需要的是**全新领域技术实践，或者深度原理研究**。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42b16882-782d-40cc-b120-c6dd4299147e",
   "metadata": {},
   "source": [
    "今天我们来抛砖引玉，节选深度原理进阶的部分来向大家解答两个问题——\n",
    "\n",
    "**Q1：数学证明：RNN为什么容易梯度消失和梯度爆炸？**\n",
    "\n",
    "**Q2：数学证明：LSTM解决了梯度消失和梯度爆炸问题吗？**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f06fe611-5abc-4e72-8504-2f0a05f3f35d",
   "metadata": {},
   "source": [
    "让我们先来看第一个问题——"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28081f62-c181-4154-b7ad-1a583fac5b0b",
   "metadata": {},
   "source": [
    "### 3.1 数学探讨：RNN为什么容易梯度消失和梯度爆炸？"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "833a6210-6b70-46e3-8c9c-05b1dc478c85",
   "metadata": {},
   "source": [
    "梯度消失和梯度爆炸是RNN在反向传播过程中常见的问题，RNN的反向传播是**通过时间的反向传播”（Backpropagation Through Time，BPTT）**，其运行流程与一般的反向传播大有不同。在之前的课程中我们提到，在不同类型NLP任务会有不同的输出层结构、会有不同的标签输出方式。例如，在对词语/样本进行预测的任务中（情感分类、词性标注、时间序列等任务），RNN**会在每个时间步都输出词语对应的相应预测标签**；但是，在对句子进行预测的任务中（例如，生成式任务、seq2seq的任务、或以句子为单位进行标注、分类的任务），RNN很可能**只会在最后一个时间步输出句子相对应的预测标签**。输出标签的方式不同，反向传播的流程自然会有所区别。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27db3cb5-d846-40ee-b9f6-2dc383cb0a1f",
   "metadata": {},
   "source": [
    "![](http://skojiangdoc.oss-cn-beijing.aliyuncs.com/2023DL/Live/NLP%26LLMs/14.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "caa39abb-969c-4c52-a01a-80477080401c",
   "metadata": {},
   "source": [
    "假设现在我们有一个最为简单的RNN，需要完成针对每个词语的情感分类任务。该RNN由输入层、一个隐藏层和一个输出层构成，全部层都没有截距项，总共循环$t$个时间步。该网络的输入数据为$X$，输出的预测标签为$\\hat{y}$，真实标签为$y$，激活函数为$\\sigma$，输入层与隐藏层之间的权重矩阵为$W_{xh}$，隐藏层与输出层之间的权重矩阵为$W_{hy}$，隐藏层与隐藏层之间的权重为$W_{hh}$，损失函数为$L(\\hat{y},y)$，t时刻的损失函数我们简写为$L_t$。此时，在时间步t上，这个RNN的**正向传播过程**可以展示如下："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "63047391-c876-4373-8dc8-98c613982759",
   "metadata": {},
   "source": [
    "> 时间步$t$\n",
    "\n",
    "$$\n",
    "\\begin{align*}\n",
    "\\mathbf{h}_{t} &= \\sigma(\\mathbf{W}_{xh} \\mathbf{X}_{t} + \\mathbf{W}_{hh} \\color{red}{\\mathbf{h}_{t-1}}) \\\\\n",
    "& = \\sigma(\\mathbf{W}_{xh} \\mathbf{X}_t + \\mathbf{W}_{hh} \\color{red}{\\sigma(\\mathbf{W}_{xh} \\mathbf{X}_{t-1} + \\mathbf{W}_{hh} \\mathbf{h}_{t-2})}) \\\\ \\\\ \n",
    "\\mathbf{\\hat{y}}_{t} &= \\mathbf{W}_{hy} \\mathbf{h}_{t} \\\\ \\\\ \n",
    "L_{t} &= L(\\mathbf{\\hat{y}}_{t}，\\mathbf{y}_{t})\n",
    "\\end{align*}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1e9b154-2ce2-4271-a718-7e78e8447ec3",
   "metadata": {},
   "source": [
    "不难发现，**RNN中存在至少三个权重矩阵需要迭代**：输入层与隐藏层之间的权重矩阵$W_{xh}$，隐藏层与输出层之间的权重矩阵$W_{hy}$，隐藏层与隐藏层之间的权重$W_{hh}$。当完成正向传播后，我们需要在反向传播过程中对以上三个权重求解梯度、并迭代权重，以$W_{hh}$为例——\n",
    "\n",
    "> 时间步t，我们需要求解的其中一个梯度为：\n",
    "\n",
    "$$\n",
    "\\begin{align*}\n",
    "3.1）\\frac{\\partial L_{t}}{\\partial W_{hh}}\\\\ \\\\\n",
    "\\end{align*}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "72b78c3c-2e36-40c0-a66a-6dda64b23d44",
   "metadata": {},
   "source": [
    "根据之前的数学流程，$L_{t}$可以展开展示为：\n",
    "\n",
    "$$\n",
    "\\begin{align*}\n",
    "L_{t} &= L(\\mathbf{\\hat{y}}_{t}, \\mathbf{y}_{t}) \\\\ \\\\\n",
    "&=L(\\mathbf{W}_{hy} \\mathbf{h}_{t}, \\mathbf{y}_{t}) \\\\ \\\\\n",
    "&=L(\\mathbf{W}_{hy} \\sigma(\\mathbf{W}_{xh} \\mathbf{X}_{t} + \\mathbf{W}_{hh} \\mathbf{h}_{t-1}), \\mathbf{y}_{t})\n",
    "\\end{align*}\n",
    "$$\n",
    "\n",
    "可见，$L_{t}$是以$\\hat{y}_{t}$为自变量的函数，$\\hat{y}_{t}$是以$W_{hy}$和$h_{t}$为自变量的函数，$h_{t}$又是以$W_{xh}$和$W_{hh}$为自变量的函数，因此要求解上面三个梯度，其实是需要对复合函数进行求导。根据链式法则规则，如果y = f(u)并且u = g(x)，那y直接对x求导的公式则可写成：\n",
    "\n",
    "$$\n",
    "\\frac{dy}{dx} = \\frac{dy}{du} \\cdot \\frac{du}{dx}\n",
    "$$\n",
    "\n",
    "因此根据链式法则，我们有：\n",
    "\n",
    "$$\n",
    "\\begin{align*}\n",
    "1) \\ \\frac{\\partial L_{t}}{\\partial W_{hy}} &= \\frac{\\partial L_{t}}{\\partial \\hat{y}_{t}} * \\frac{\\partial \\hat{y}_{t}}{\\partial W_{hy}} \\\\ \\\\\n",
    "2.1) \\ \\frac{\\partial L_{t}}{\\partial W_{xh}} &= \\frac{\\partial L_{t}}{\\partial \\hat{y}_{t}} * \\frac{\\partial \\hat{y}_{t}}{\\partial h_{t}} * \\frac{\\partial h_{t}}{\\partial W_{xh}} \\\\ \\\\\n",
    "3.1) \\  \\frac{\\partial L_{t}}{\\partial W_{hh}} &= \\frac{\\partial L_{t}}{\\partial \\hat{y}_{t}} * \\frac{\\partial \\hat{y}_{t}}{\\partial h_{t}} * \\frac{\\partial h_{t}}{\\partial W_{hh}}\n",
    "\\end{align*}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bfe90dcf-47ba-4ecd-b592-fcf8bbb3af71",
   "metadata": {},
   "source": [
    "好了，到这里为止循环神经网络的反向传播过程都与普通深度神经网络类似，但有的小伙伴可能已经注意到了，上面的公式2.1和3.1中存在一个关键问题，那就是$h_t$作为一个复合函数，不止能以$W_{xh}$和$W_{hh}$为自变量，还能以上层的隐藏状态$h_{t-1}$作为自变量，而$h_{t-1}$本身又是以$W_{xh}$和$W_{hh}$为自变量的函数：\n",
    "\n",
    "$$\n",
    "\\begin{align*}\n",
    "L_{t} &= L(\\mathbf{\\hat{y}}_{t}, \\mathbf{y}_{t}) \\\\ \\\\\n",
    "&=L(\\mathbf{W}_{hy} \\mathbf{h}_{t}, \\mathbf{y}_{t}) \\\\ \\\\\n",
    "&= L(\\mathbf{W}_{hy} \\sigma(\\mathbf{W}_{xh} \\mathbf{X}_{t} + \\mathbf{W}_{hh} \\color{red}{\\mathbf{h}_{t-1}}), \\mathbf{y}_{t})  \\\\ \\\\\n",
    "&= L(\\mathbf{W}_{hy} \\sigma(\\mathbf{W}_{xh} \\mathbf{X}_{t} + \\mathbf{W}_{hh} \\color{red}{\\sigma(\\mathbf{W}_{xh} \\mathbf{X}_{t} + \\mathbf{W}_{hh} \\mathbf{h}_{t-2})},\\mathbf{y}_{t})\n",
    "\\end{align*}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db2ddb64-5fc2-4d0e-b80f-8f5323a7d0b4",
   "metadata": {},
   "source": [
    "此时你发现了吗？在求解$L_{t}$对$W_{hh}$的导数时，不止可以求解上面所写的式子3.1，还可以继续对嵌套函数求解得到下面的梯度3.2——\n",
    "\n",
    "$$\n",
    "\\begin{align*}\n",
    "\\color{red}{3.2)} \\  \\frac{\\partial L_{t}}{\\partial W_{hh}} &= \\frac{\\partial L_{t}}{\\partial \\hat{y}_{t}} * \\frac{\\partial \\hat{y}_{t}}{\\partial h_{t}} * \\frac{\\partial h_{t}}{\\partial h_{t-1}} * \\frac{\\partial h_{t-1}}{\\partial W_{hh}} \\\\ \\\\\n",
    "\\end{align*}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3bb8e9bc-a948-4b28-bc5a-2a9f24d5c88e",
   "metadata": {},
   "source": [
    "甚至，我们还可以将$h_{t-1}$继续拆解为$\\sigma(\\mathbf{W}_{xh} \\mathbf{X}_{t-1} + \\mathbf{W}_{hh} \\mathbf{h}_{t-2})$，还可以将$h_{t-2}$继续拆解为$\\sigma(\\mathbf{W}_{xh} \\mathbf{X}_{t-2} + \\mathbf{W}_{hh} \\mathbf{h}_{t-3})$，我们可以将嵌套函数无止尽地拆解下去，直到拆到$\\mathbf{h}_1 = \\sigma(\\mathbf{W}_{xh} \\mathbf{X}_1 + \\mathbf{W}_{hh} \\mathbf{h}_0)$为止。在这个过程中，只要拆解足够多，我们可以从$L_{t}$求解出t个针对和$W_{hh}$的导数。因此惊人的事实是，<font color=\"red\">**在时间步t上，我们可以计算t个用于迭代$W_{xh}$和$W_{hh}$的梯度**！\n",
    "\n",
    "当我们将损失函数一直拆解到最后一层，且假设激活函数为恒等函数（为了简化数学流程）——\n",
    "\n",
    "$$\n",
    "\\begin{align*}\n",
    "L_{t} &= L(\\mathbf{\\hat{y}}_{t}, \\mathbf{y}_{t}) \\\\ \\\\\n",
    "&=L(\\mathbf{W}_{hy} \\mathbf{h}_{t}, \\mathbf{y}_{t}) \\\\ \\\\\n",
    "&=L(\\mathbf{W}_{hy} (\\mathbf{W}_{xh} \\mathbf{X}_{t} + \\mathbf{W}_{hh} \\mathbf{h}_{t-1}), \\mathbf{y}_{t}) \\\\ \\\\\n",
    "&=L(\\mathbf{W}_{hy} (\\mathbf{W}_{xh} \\mathbf{X}_{t} + \\mathbf{W}_{hh} (\\mathbf{W}_{xh} \\mathbf{X}_{t-1} + \\mathbf{W}_{hh} \\mathbf{h}_{t-2})), \\mathbf{y}_{t}) \\\\ \\\\\n",
    "& \\vdots \\\\ \\\\\n",
    "&=L(\\mathbf{W}_{hy} (\\mathbf{W}_{xh} \\mathbf{X}_{t} + \\mathbf{W}_{hh} (\\mathbf{W}_{xh} \\mathbf{X}_{t-1} + \\dots + \\mathbf{W}_{hh} (\\mathbf{W}_{xh} \\mathbf{X}_{1} + \\mathbf{W}_{hh} \\mathbf{h}_{0}))...), \\mathbf{y}_{t})\n",
    "\\end{align*}\n",
    "$$\n",
    "    \n",
    "\n",
    "<br>\n",
    "\n",
    "在这个彻底拆解后的公式上，我们可以求解出嵌套了t层的$W_{hh}$的梯度（如公式3.t）：\n",
    "\n",
    "<br>\n",
    "\n",
    "$$\n",
    "\\begin{align*}\n",
    "{3.t)} \\  \\frac{\\partial L_{t}}{\\partial W_{hh}} &= \\frac{\\partial L_{t}}{\\partial \\hat{y}_{t}} * \\frac{\\partial \\hat{y}_{t}}{\\partial h_{t}} * \\frac{\\partial h_{t}}{\\partial h_{t-1}} * \\frac{\\partial h_{t-1}}{\\partial h_{t-2}} * \\ \\  ... \\ \\ * \\frac{\\partial h_2}{\\partial h_1} * \\frac{\\partial h_1}{\\partial W_{hh}} \\\\ \\\\\n",
    "\\end{align*}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4eedd5ba-9e93-47a1-9d0f-ddc7b2c2a82d",
   "metadata": {},
   "source": [
    "此时在这个公式中，许多偏导数的求解就变得非常简单，例如：\n",
    "\n",
    "$$\n",
    "\\begin{align*}\n",
    "&\\because {\\hat{y}}_{t} = W_{hy} h_{t},\\ \\ \\therefore \\frac{\\partial \\hat{y}_{t}}{\\partial h_{t}} = {W}_{hy}\n",
    "\\\\ \\\\\n",
    "&\\because {h}_{t} = {W}_{xh} {X}_{t} + {W}_{hh} {h}_{t-1}, \\ \\ \\therefore \\frac{\\partial h_{t}}{\\partial h_{t-1}} = {W}_{hh} \\\\ \\\\\n",
    "&\\because {h}_{t-1} = {W}_{xh} {X}_{t-1} + {W}_{hh} {h}_{t-2}, \\ \\ \\therefore \\frac{\\partial h_{t-1}}{\\partial h_{t-2}} = {W}_{hh} \\\\ \\\\\n",
    "& \\vdots \\\\ \\\\\n",
    "&\\because {h}_2 = {W}_{xh} {X}_{2} + {W}_{hh} {h}_{1}, \\ \\ \\therefore \\frac{\\partial h_2}{\\partial h_1} = {W}_{hh} \\\\ \\\\\n",
    "&\\because {h}_1 = {W}_{xh} {X}_{1} + {W}_{hh} {h}_{0}, \\ \\ \\therefore \\frac{\\partial h_1}{\\partial {W}_{hh}} = {h}_{0}\n",
    "\\end{align*}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4e7134bb-b288-40c9-b771-204186fd4265",
   "metadata": {},
   "source": [
    "所以最终的梯度表达式为：\n",
    "\n",
    "$$\n",
    "\\begin{align*}\n",
    "{3.t)} \\ \\frac{\\partial L_{t}}{\\partial W_{hh}}\n",
    "&= \\frac{\\partial L_{t}}{\\partial \\hat{y}_{t}} * \\frac{\\partial \\hat{y}_{t}}{\\partial h_{t}} * \\frac{\\partial h_{t}}{\\partial h_{t-1}} * \\frac{\\partial h_{t-1}}{\\partial h_{t-2}} * \\ \\  ... \\ \\ * \\frac{\\partial h_2}{\\partial h_1} * \\frac{\\partial h_1}{\\partial W_{hh}} \\\\ \\\\\n",
    "&= \\frac{\\partial L_{t}}{\\partial \\hat{y}_{t}} * W_{hy} * W_{hh} * W_{hh} * \\ \\  ... \\ \\ * W_{hh} * h_0 \\\\ \\\\\n",
    "&= \\frac{\\partial L_{t}}{\\partial \\hat{y}_{t}} * W_{hy} * (W_{hh})^{t-1}* h_0 \\\\ \\\\\n",
    "\\end{align*}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4e8ad9de-e451-4f40-93de-8d1d5f7fff72",
   "metadata": {},
   "source": [
    "不难发现，在这个梯度表达式中出现了$(W_{hh})^{t-1}$这样的高次项，这就是循环神经网络非常容易梯度爆炸和梯度消失的根源所在——假设$W_{hh}$是一个小于1的值，那$(W_{hh})^{t-1}$将会非常接近于0，从而导致梯度消失；假设$W_{hh}$大于1，那$(W_{hh})^{t-1}$将会接近无穷大，从而引发梯度爆炸，其中梯度消失发生的可能性又远远高于梯度爆炸。\n",
    "\n",
    "在深度神经网络中，在应用链式法则后，我们也会面临复合函数梯度连乘的问题，但**由于普通神经网络中并不存在“权值共享”的现象，因此每个偏导数的表达式求解出的值大多是不一致的**，在连乘的时候有的偏导数值较大、有的偏导数值较小，相比之下就不那么容易发生梯度爆炸或梯度消失问题的问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6be47395-c783-464f-8e9a-87b375223286",
   "metadata": {},
   "source": [
    "### 3.2 数学探讨：LSTM能够解决梯度消失和梯度爆炸问题吗？"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "09ca7aeb-043e-440f-a6ad-1c6e04f856a3",
   "metadata": {},
   "source": [
    "你或许很早就听说过，LSTM能够很好地解决RNN的梯度消失/梯度爆炸问题，甚至你可能人为LSTM是为了解决梯度消失和梯度爆炸而诞生的，但我们在进行深度原理研究的过程中却发现并非如此。让我们来简单梳理一下LSTM的数学流程——\n",
    "\n",
    "遗忘门：$$f_t = \\sigma(W_{xf} \\cdot x_t + W_{hf} \\cdot h_{t-1})$$\n",
    "\n",
    "输入门：$$i_t = \\sigma(W_{xi} \\cdot x_t + W_{hi} \\cdot h_{t-1})$$\n",
    "\n",
    "潜在细胞状态：$$\\tilde{C}_t = \\tanh(W_{xc} \\cdot x_t + W_{hc} \\cdot h_{t-1})$$\n",
    "\n",
    "细胞状态更新：$$C_t = f_t \\cdot C_{t-1} + i_t \\cdot \\tilde{C}_t$$\n",
    "\n",
    "输出门：$$o_t = \\sigma(W_{xo} \\cdot x_t + W_{ho} \\cdot h_{t-1})$$\n",
    "\n",
    "当下时刻输出：$$h_t = o_t \\cdot \\tanh(C_t)$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d52f761-abd8-455e-9c7e-bb85a8e31119",
   "metadata": {},
   "source": [
    "**LSTM在反向传播中的梯度求解链路可以被可视化为**："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aa952a0b-e914-4a77-82bf-59e2b6a6a0f4",
   "metadata": {},
   "source": [
    "![](https://fufanshare.oss-cn-beijing.aliyuncs.com/wangshuai/lstm.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "418f48d1-1a4c-4d31-9835-087c806217bc",
   "metadata": {},
   "source": [
    "假设我们现在计算$\\frac{\\partial L_{t}}{\\partial W_{hf}}$，则有——"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "424956d0-8c7a-425b-a54d-be7be81d9bfa",
   "metadata": {},
   "source": [
    "$$\n",
    "\\begin{align*}\n",
    "{3.t)} \\ \\frac{\\partial L_{t}}{\\partial W_{hf}}\n",
    "&= \\frac{\\partial L_{t}}{\\partial \\hat{y}_{t}} * \\frac{\\partial \\hat{y}_{t}}{\\partial h_{t}} * \\frac{\\partial h_{t}}{\\partial C_{t}} * \\frac{\\partial C_{t}}{\\partial f_{t}} * \\frac{\\partial f_{t}}{\\partial h_{t-1}}* \\frac{\\partial h_{t-1}}{\\partial C_{t-1}}\\ \\  ... \\ \\ * \\frac{\\partial f_1}{\\partial W_{hf}} \\\\ \\\\\n",
    "&= \\frac{\\partial L_{t}}{\\partial \\hat{y}_{t}} \\frac{\\partial \\hat{y}_{t}}{\\partial h_{t}} * \\frac{\\partial h_{t}}{\\partial C_{t}} * [ \\frac{\\partial C_t}{\\partial C_{t-1}} * \\frac{\\partial C_{t-1}}{\\partial C_{t-2}} \\ \\  ... \\ \\ * \\frac{\\partial C_2}{\\partial C_{1}}] * \\frac{\\partial C_1}{\\partial f_1} * \\frac{\\partial f_1}{\\partial w_{hf}} \\\\ \\\\\n",
    "&= \\frac{\\partial L_{t}}{\\partial \\hat{y}_{t}} \\frac{\\partial \\hat{y}_{t}}{\\partial h_{t}} * \\frac{\\partial h_{t}}{\\partial C_{t}} * [f_t * f_{t-1} * f_{t-2} ... * f_1] * \\frac{\\partial C_1}{\\partial f_1} * \\frac{\\partial f_1}{\\partial w_{hf}} \\\\ \\\\\n",
    "\\end{align*}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "54e4523e-c319-4df6-94b3-5f4cb3b8b5bf",
   "metadata": {},
   "source": [
    "通过避开共享权重的相乘，LSTM将循环网络梯度爆炸和梯度消失的危险性降低到了一般神经网络的水平。由于$f_t$在0～1之间，因此就意味着梯度爆炸的风险将会很小，至于会不会梯度消失，取决于$f_t$是否接近于1。如果当下时刻的长期记忆比较依赖于历史信息，那么$f_t$就会接近于1，这时候历史的梯度信息也正好不容易消失；如果$f_t$很接近于0，那么就说明当下的长期记忆不依赖于历史信息，这时候就算梯度消失也无妨了。\n",
    "\n",
    "$f_t$在0～1之间这个特性决定了它梯度爆炸的风险很小，同时$f_t$表明了模型对历史信息的依赖性，也正好是历史梯度的保留程度，两者相互自洽，所以LSTM也能较好地缓解梯度消失问题。因此，LSTM同时较好地缓解了梯度消失/爆炸问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40177865-e5ab-4eaa-81ed-7864fecda700",
   "metadata": {},
   "source": [
    "【有趣的小事实】\n",
    "\n",
    "在今天看来，梯度消失和梯度爆炸是阻碍RNN能够被正常训练的关键问题，但在1990年代，算力和数据都十分缺乏、神经网络层数往往较浅、在研究中所需的迭代次数很少，序列长度也较短，因此RNN的梯度消失和梯度爆炸问题并没有这么显著。相对的，当时大量的研究（包括LSTM）都围绕着两个更加显著、更加直观的问题展开，其中一个问题是**权重冲突（Weight Conflict）**，另一个问题是**长短期信息冲突（Long-Short Term Information Conflict）**。所以当我们追溯LSTM的结构与解决梯度消失、梯度爆炸问题有任何关联的时候，其实是无法找到其中的关联的。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40dc3d37-5eb4-476d-abfa-3a4976b4f8f4",
   "metadata": {},
   "source": [
    "# 【公开课下周继续】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a9e5a55e-61e9-46da-a12a-5311f690ac07",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
